﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace AOUtils.Libraries
{
    public sealed class SearchEngineCompletedEventArgs : EventArgs
    {
        private readonly OptimizedItem[] items;

        public IEnumerable<OptimizedItem> Items
        {
            get { return items; }
        }

        public SearchEngineCompletedEventArgs(List<OptimizedItem> operationResult)
        {
            this.items = operationResult.ToArray();
        }
    }

    public sealed class SearchEngineProgressEventArgs : EventArgs
    {
        private readonly OptimizedItem item;

        public OptimizedItem Item
        {
            get { return item; }
        }

        public SearchEngineProgressEventArgs(OptimizedItem item)
        {
            this.item = item;
        }
    }

    public class SearchEngine
    {
        public event EventHandler<SearchEngineCompletedEventArgs> OnEndWork;
        public event EventHandler<SearchEngineProgressEventArgs> OnWorkProgress;

        private static BackgroundWorker engine;

        private readonly Breed breed;
        private readonly Professions profession;
        private readonly Faction faction;
        private readonly string skill;
        private readonly bool useSocial;
        private readonly OptimizedXml xml;

        private List<OptimizedItem> itemStats = new List<OptimizedItem>();

        private BackgroundWorker currentBackgroundWorker;

        public SearchEngine(Breed breed,Professions profession,Faction faction,string skill,bool useSocial,OptimizedXml xml)
        {
            this.breed = breed;
            this.profession = profession;
            this.faction = faction;
            this.skill = skill;
            this.useSocial = useSocial;
            this.xml = xml;

            if (engine == null)
            {
                engine = new BackgroundWorker();
                engine.DoWork += new DoWorkEventHandler(engine_DoWork);
                engine.RunWorkerCompleted += new RunWorkerCompletedEventHandler(engine_RunWorkerCompleted);
                engine.WorkerReportsProgress = true;
                engine.ProgressChanged += new ProgressChangedEventHandler(engine_ProgressChanged);
                engine.WorkerSupportsCancellation = true;
            }
        }

        public bool StartWork()
        {
            if (!engine.IsBusy)
            {
                engine.RunWorkerAsync(this);
                return true;
            }
            else
                return false;
        }
        public bool IsBusy
        {
            get { return engine.IsBusy; }
        }
        public void RequestCancel()
        {
            if (currentBackgroundWorker != null)
                currentBackgroundWorker.CancelAsync();
        }

        private void engine_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (OnEndWork != null)
            {
                OnEndWork(this, new SearchEngineCompletedEventArgs(itemStats));
            }
            currentBackgroundWorker = null;
            engine = null;
        }

        private void engine_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            OptimizedItem item = e.UserState as OptimizedItem;
            if (OnWorkProgress != null)
                OnWorkProgress(this, new SearchEngineProgressEventArgs(item));
            itemStats.Add(item);
        }

        private static void engine_DoWork(object sender, DoWorkEventArgs e)
        {
            SearchEngine args = e.Argument as SearchEngine;
            args.currentBackgroundWorker = sender as BackgroundWorker;
            args.DoWork();
        }

        private void DoWork()
        {
            OptimizedXml.Predicate<OptimizedItem, Faction> factionFilter = AnalyzeFactionReq;
            OptimizedXml.Predicate<OptimizedItem, Breed> breedFilter = AnalyzeBreedReq;
            OptimizedXml.Predicate<OptimizedItem, Professions> professionFilter = AnalyzeProfessionReq;
            IEnumerable<OptimizedItem> items =
                xml.Filter<OptimizedItem, Breed>(
                xml.Filter<OptimizedItem, Professions>(
                xml.Filter<OptimizedItem, Faction>(
                xml.HasSkill(xml.Items, skill),
                factionFilter, faction),
                professionFilter, profession),
                breedFilter, breed);

            if (currentBackgroundWorker != null)
                foreach (OptimizedItem item in items)
                {
                    item.SortSkill = skill;
                    currentBackgroundWorker.ReportProgress(0, item);
                    if (currentBackgroundWorker.CancellationPending)
                        return;
                }
            else
                itemStats.AddRange(items);
        }
        private bool AnalyzeProfessionReq(OptimizedItem item, Professions profession)
        {
            bool returnValue = true;
            foreach (OptimizedRequirement req in item.HasProfessionReq)
            {
                if (((req.Operator & OperatorType.EqualTo) > 0) &&
                    (((Professions)Enum.Parse(typeof(Professions), req.Value.Replace(" ", "").Replace("-", ""))) & profession) > 0)
                {
                    returnValue = true;
                    break;
                }
                else if (((req.Operator & OperatorType.EqualTo) > 0) &&
                    (((Professions)Enum.Parse(typeof(Professions), req.Value.Replace(" ", "").Replace("-", ""))) & profession) == 0)
                    returnValue = false;
                if (((req.Operator & OperatorType.NotEqualTo) > 0) &&
                    (((Professions)Enum.Parse(typeof(Professions), req.Value.Replace(" ", "").Replace("-", ""))) & profession) > 0)
                {
                    returnValue = false;
                    break;
                }
                else if (((req.Operator & OperatorType.NotEqualTo) > 0) &&
                    (((Professions)Enum.Parse(typeof(Professions), req.Value.Replace(" ", "").Replace("-", ""))) & profession) == 0)
                    returnValue = true;
            }
            return returnValue;
        }
        private bool AnalyzeBreedReq(OptimizedItem item, Breed breed)
        {
            bool returnValue = true;
            foreach (OptimizedRequirement req in item.HasBreedReq)
            {
                if (((req.Operator & OperatorType.EqualTo) > 0) &&
                    (((Breed)Enum.Parse(typeof(Breed), req.Value)) & breed) > 0)
                {
                    returnValue = true;
                    break;
                }
                else if (((req.Operator & OperatorType.EqualTo) > 0) &&
                    (((Breed)Enum.Parse(typeof(Breed), req.Value)) & breed) == 0)
                    returnValue = false;
                if (((req.Operator & OperatorType.NotEqualTo) > 0) &&
                    (((Breed)Enum.Parse(typeof(Breed), req.Value)) & breed) > 0)
                {
                    returnValue = false;
                    break;
                }
                else if (((req.Operator & OperatorType.NotEqualTo) > 0) &&
                    (((Breed)Enum.Parse(typeof(Breed), req.Value)) & breed) == 0)
                    returnValue = true;
            }
            return returnValue;
        }
        private bool AnalyzeFactionReq(OptimizedItem item, Faction faction)
        {
            bool returnValue = true;
            foreach (OptimizedRequirement req in item.HasFactionReq)
            {
                if (((req.Operator & OperatorType.EqualTo) > 0) &&
                    (((Faction)Enum.Parse(typeof(Faction), req.Value)) & faction) > 0)
                {
                    returnValue = true;
                    break;
                }
                else if (((req.Operator & OperatorType.EqualTo) > 0) &&
                    (((Faction)Enum.Parse(typeof(Faction), req.Value)) & faction) == 0)
                    returnValue = false;
                if (((req.Operator & OperatorType.NotEqualTo) > 0) &&
                    (((Faction)Enum.Parse(typeof(Faction), req.Value)) & faction) > 0)
                {
                    returnValue = false;
                    break;
                }
                else if (((req.Operator & OperatorType.NotEqualTo) > 0) &&
                    (((Faction)Enum.Parse(typeof(Faction), req.Value)) & faction) == 0)
                    returnValue = true;
            }
            return returnValue;
        }
    }
}
